package com.vitalfit.service.serviceImpl;

import com.vitalfit.client.UserServiceClient;
import com.vitalfit.enums.OrderStateEnum;
import com.vitalfit.model.dto.FoodOrderDTO;
import com.vitalfit.model.dto.FoodOrderDetailDTO;
import com.vitalfit.model.entity.food.FoodCustomization;
import com.vitalfit.model.entity.food.FoodItem;
import com.vitalfit.model.entity.food.FoodOrder;
import com.vitalfit.model.entity.food.FoodOrderDetail;
import com.vitalfit.model.entity.user.Customer;
import com.vitalfit.model.vo.PageFoodOrderVO;
import com.vitalfit.model.vo.UserAuthentication;
import com.vitalfit.repository.FoodCustomizationRepository;
import com.vitalfit.repository.FoodItemRepository;
import com.vitalfit.repository.FoodOrderRepository;
import com.vitalfit.service.FoodOrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class FoodOrderServiceImpl implements FoodOrderService {


    private final FoodOrderRepository foodOrderRepository;

    private final UserServiceClient userServiceClient;

    private final FoodCustomizationRepository foodCustomizationRepository;

    private final FoodItemRepository foodItemRepository;

    /**
     * 根据用户认证信息和订单DTO创建食品订单。
     * 此方法使用Java Stream API计算订单总金额，并使用CompletableFuture异步调用外部服务。
     *
     * @param userAuthentication 用户认证信息。
     * @param foodOrderDTO       订单数据传输对象。
     * @return 已持久化的FoodOrder实体。
     */
    @Override
    @Transactional
    public FoodOrder createFoodOrder(UserAuthentication userAuthentication, FoodOrderDTO foodOrderDTO) {
        // 异步获取客户信息。
        CompletableFuture<Customer> customerFuture = CompletableFuture.supplyAsync(() ->
                userServiceClient.getCustomerById(userAuthentication.getToken(), userAuthentication.getUserId()));

        // 初始化FoodOrder实体并设置时间和初始状态。
        FoodOrder foodOrder = new FoodOrder();
        foodOrder.setCreateDateTime(LocalDateTime.now());
        foodOrder.setOrderStateEnum(OrderStateEnum.PENDING);

        // 处理订单详情并计算总金额。
        List<CompletableFuture<FoodOrderDetail>> detailsFutures = foodOrderDTO.getFoodOrderDetailDTOS().stream()
                .map(detailDTO -> CompletableFuture.supplyAsync(() ->
                        createFoodOrderDetail(userAuthentication, detailDTO, foodOrder), Executors.newCachedThreadPool()))
                .toList();

        CompletableFuture<Void> allFutures = CompletableFuture.allOf(detailsFutures.toArray(new CompletableFuture[0]));

        // 使用CompletableFuture的thenCompose等待所有异步操作完成，并继续执行。
        return allFutures.thenCompose(v -> {
            List<FoodOrderDetail> foodOrderDetails = detailsFutures.stream()
                    .map(CompletableFuture::join)
                    .collect(Collectors.toList());

            BigDecimal totalCost = foodOrderDetails.stream()
                    .map(FoodOrderDetail::getSubtotal)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            foodOrder.setFoodOrderDetails(foodOrderDetails);
            foodOrder.setTotalCost(totalCost);

            // 确保客户信息已加载并设置到订单中。
            return customerFuture.thenApply(customer -> {
                foodOrder.setCustomer(customer);
                return foodOrderRepository.save(foodOrder);
            });
        }).join();
    }

    /**
     * 从DTO创建订单详情的辅助方法。
     * 此方法调用食品服务客户端以获取食品项信息，并计算小计。
     *
     * @param userAuthentication 用户认证信息，用于服务调用。
     * @param detailDTO          订单详情DTO。
     * @param foodOrder          所属的食品订单。
     * @return 创建的FoodOrderDetail实体。
     */
    private FoodOrderDetail createFoodOrderDetail(UserAuthentication userAuthentication, FoodOrderDetailDTO detailDTO,
                                                  FoodOrder foodOrder) {

        FoodItem foodItem = foodItemRepository.findById(detailDTO.getFoodItemId()).orElseThrow(
                () -> new RuntimeException("Food not found")
        );

        BigDecimal basePrice = foodItem.getBasePrice().multiply(BigDecimal.valueOf(detailDTO.getQuantity()));
        AtomicReference<BigDecimal> subtotal = new AtomicReference<>(basePrice);

        // 处理定制选项，获取每个定制选项并计算总额
        List<FoodCustomization> chosenCustomizations = detailDTO.getCustomizationIds().stream()
                .flatMap(customizationId -> {
                    // 使用findById获取Optional<FoodCustomization>
                    Optional<FoodCustomization> optionalCustomization = foodCustomizationRepository.findById(customizationId);

                    // 如果Optional有值，则计算额外费用并更新子计
                    return optionalCustomization.map(customization -> {
                        BigDecimal additionalCost = customization.getAdditionalPrice().multiply(BigDecimal.valueOf(detailDTO.getQuantity()));
                        // 更新小计，使用AtomicReference
                        subtotal.updateAndGet(v -> v.add(additionalCost));
                        return customization;
                    }).stream(); // 如果Optional没有值，则返回一个空的Stream
                })
                .collect(Collectors.toList());


        // 创建FoodOrderDetail实体
        FoodOrderDetail foodOrderDetail = new FoodOrderDetail();
        foodOrderDetail.setFoodOrder(foodOrder);
        foodOrderDetail.setFoodItem(foodItem);
        foodOrderDetail.setQuantity(detailDTO.getQuantity());
        foodOrderDetail.setChosenCustomizations(chosenCustomizations);
        foodOrderDetail.setSubtotal(subtotal.get());


        // 直接返回未持久化的FoodOrderDetail，让事务在主方法中统一处理
        return foodOrderDetail;
    }

    /**
     * 更新订单状态
     * @param foodOrderDTO 订单的数据传输对象，包含订单ID和新状态
     * @return 更新后的订单实体
     */
    @Override
    @Transactional
    public FoodOrder updateFoodOrderStatus(FoodOrderDTO foodOrderDTO) {
        FoodOrder foodOrder = foodOrderRepository.findById(foodOrderDTO.getId())
                .orElseThrow(() -> new IllegalArgumentException("Order not found：" + foodOrderDTO.getId()));

        // 通过描述获取订单状态，确保状态有效
        OrderStateEnum state = OrderStateEnum.getOrderStateByDesc(foodOrderDTO.getOrderState());

        foodOrder.setOrderStateEnum(state);
        return foodOrderRepository.save(foodOrder);
    }

    /**
     * 为订单评分
     * @param orderId 订单ID
     * @param rating 评分，范围1到5
     * @return 更新后的订单实体
     */
    @Override
    @Transactional
    public FoodOrder rateFoodOrder(Integer orderId, Integer rating) {
        FoodOrder foodOrder = foodOrderRepository.findById(orderId)
                .orElseThrow(() -> new IllegalArgumentException("Order not found：" + orderId));

        foodOrder.setStarRating(rating);
        return foodOrderRepository.save(foodOrder);
    }

    /**
     * 获取指定ID的订单
     * @param orderId 订单ID
     * @return 订单实体
     */
    @Override
    @Transactional(readOnly = true)
    public FoodOrder getFoodOrder(Integer orderId) {
        return foodOrderRepository.findById(orderId)
                .orElseThrow(() -> new IllegalArgumentException("Order not found：" + orderId));
    }

    @Override
    @Transactional(readOnly = true)
    public PageFoodOrderVO getFoodOrderByUserId(Integer userId, int page, int size) {

        // Create pageable object to apply pagination
        Pageable pageable = PageRequest.of(page, size);

        Page<FoodOrder> foodOrderPage = foodOrderRepository.findAllByCustomerId(userId,pageable);

        // Map page data to VO
        PageFoodOrderVO  pageFoodOrderVO = new PageFoodOrderVO();
        pageFoodOrderVO.setTotalPages(foodOrderPage.getTotalPages());
        pageFoodOrderVO.setTotalElements(foodOrderPage.getTotalElements());
        pageFoodOrderVO.setContent(foodOrderPage.getContent());
        return pageFoodOrderVO;

    }

    @Override
    @Transactional(readOnly = true)
    public PageFoodOrderVO getAllFoodOrder(int page, int size) {

        // Create pageable object to apply pagination
        Pageable pageable = PageRequest.of(page, size);

        Page<FoodOrder> foodOrderPage = foodOrderRepository.findAll(pageable);

        // Map page data to VO
        PageFoodOrderVO  pageFoodOrderVO = new PageFoodOrderVO();
        pageFoodOrderVO.setTotalPages(foodOrderPage.getTotalPages());
        pageFoodOrderVO.setTotalElements(foodOrderPage.getTotalElements());
        pageFoodOrderVO.setContent(foodOrderPage.getContent());
        return pageFoodOrderVO;

    }
}


